package com.gitee.wsl.base

import com.gitee.wsl.base.ReferenceEntry.Companion.nullEntry
import kotlinx.atomicfu.atomic

/**
 * A reference to a value.
 */
interface ValueReference<K : Any, V : Any> {
    /**
     * Returns the value. Does not block or throw exceptions.
     */
    fun get(): V?

    /**
     * Returns the weight of this entry. This is assumed to be static between calls to setValue.
     */
    val weight: Int

    /**
     * Returns the entry associated with this value reference, or `null` if this value
     * reference is independent of any entry.
     */
    val entry: ReferenceEntry<K, V>?

    /**
     * Creates a copy of this reference for the given entry.
     *
     *
     *
     * `value` may be null only for a loading reference.
     */

    fun copyFor(value: V?, entry: ReferenceEntry<K, V>?): ValueReference<K, V>

    /**
     * Notifify pending loads that a new value was set. This is only relevant to loading
     * value references.
     */
    fun notifyNewValue(newValue: V)

    /**
     * Returns true if this reference contains an active value, meaning one that is still considered
     * present in the cache. Active values consist of live values, which are returned by cache
     * lookups, and dead values, which have been evicted but awaiting removal. Non-active values
     * consist strictly of loading values, though during refresh a value may be both active and
     * loading.
     */
    val isActive: Boolean

    companion object{
        /**
         * Placeholder. Indicates that the value hasn't been set yet.
         */
         val UNSET: ValueReference<Any, Any> = object : ValueReference<Any, Any> {
            override fun get(): Any? {
                return null
            }

            override val weight: Int
                get() = 0
            override val entry: ReferenceEntry<Any, Any>?
                get() = null

            override fun copyFor(
                value: Any?,
                entry: ReferenceEntry<Any, Any>?
            ): ValueReference<Any, Any> {
                return this
            }

            override val isActive: Boolean
                get() = false

            override fun notifyNewValue(newValue: Any) {}
        }

        /**
         * Singleton placeholder that indicates a value is being loaded.
         */
        @Suppress("UNCHECKED_CAST")
        fun <K : Any, V : Any> unset() = UNSET as ValueReference<K, V>
    }
}

/**
 * An entry in a reference map.
 *
 *
 * Entries in the map can be in the following states:
 *
 *
 * Valid:
 * - Live: valid key/value are set
 * - Loading: loading is pending
 *
 *
 * Invalid:
 * - Expired: time expired (key/value may still be set)
 * - Collected: key/value was partially collected, but not yet cleaned up
 * - Unset: marked as unset, awaiting cleanup or reuse
 */
interface ReferenceEntry<K : Any, V : Any> {
    /**
     * Returns the value reference from this entry.
     */
    /**
     * Sets the value reference for this entry.
     */
    var valueReference: ValueReference<K, V>?
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()

    /**
     * Returns the next entry in the chain.
     */
    val next: ReferenceEntry<K, V>?
        get() = throw UnsupportedOperationException()

    /**
     * Returns the entry's hash.
     */
    val hash: Int
        get() = throw UnsupportedOperationException()

    /**
     * Returns the key for this entry.
     */
    val key: K
        get() = throw UnsupportedOperationException()
    /*
     * Used by entries that use access order. Access entries are maintained in a doubly-linked list.
     * New entries are added at the tail of the list at write time; stale entries are expired from
     * the head of the list.
     */
    /**
     * Returns the time that this entry was last accessed, in ns.
     */
    /**
     * Sets the entry access time in ns.
     */
    var accessTime: Long
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()
    /**
     * Returns the next entry in the access queue.
     */
    /**
     * Sets the next entry in the access queue.
     */
    var nextInAccessQueue: ReferenceEntry<K, V>
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()
    /**
     * Returns the previous entry in the access queue.
     */
    /**
     * Sets the previous entry in the access queue.
     */
    var previousInAccessQueue: ReferenceEntry<K, V>
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()
    /*
     * Implemented by entries that use write order. Write entries are maintained in a
     * doubly-linked list. New entries are added at the tail of the list at write time and stale
     * entries are expired from the head of the list.
     */
    /**
     * Returns the time that this entry was last written, in ns.
     */
    /**
     * Sets the entry write time in ns.
     */
    var writeTime: Long
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()
    /**
     * Returns the next entry in the write queue.
     */
    /**
     * Sets the next entry in the write queue.
     */
    var nextInWriteQueue: ReferenceEntry<K, V>
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()
    /**
     * Returns the previous entry in the write queue.
     */
    /**
     * Sets the previous entry in the write queue.
     */
    var previousInWriteQueue: ReferenceEntry<K, V>
        get() = throw UnsupportedOperationException()
        set(_) = throw UnsupportedOperationException()

    companion object{
        @Suppress("UNCHECKED_CAST")
        fun <K : Any, V : Any> nullEntry() = NullEntry as ReferenceEntry<K, V>
    }
}

private object NullEntry : ReferenceEntry<Any, Any> {
    override var valueReference: ValueReference<Any, Any>?
        get() = null
        set(_) {}

    override val next: ReferenceEntry<Any, Any>?
        get() = null

    override val hash: Int
        get() = 0

    override val key: Any
        get() = Unit

    override var accessTime: Long
        get() = 0
        set(_) {}

    override var nextInAccessQueue: ReferenceEntry<Any, Any>
        get() = this
        set(_) {}

    override var previousInAccessQueue: ReferenceEntry<Any, Any>
        get() = this
        set(_) {}

    override var writeTime: Long
        get() = 0
        set(_) {}

    override var nextInWriteQueue: ReferenceEntry<Any, Any>
        get() = this
        set(_) {}

    override var previousInWriteQueue: ReferenceEntry<Any, Any>
        get() = this
        set(_) {}
}

/*
 * Note: All of this duplicate code sucks, but it saves a lot of memory. If only Java had mixins!
 * To maintain this code, make a change for the strong reference type. Then, cut and paste, and
 * replace "Strong" with "Soft" or "Weak" within the pasted text. The primary difference is that
 * strong entries store the key reference directly while soft and weak entries delegate to their
 * respective superclasses.
 */
/**
 * Used for strongly-referenced keys.
 */
private open class StrongEntry<K : Any, V : Any>(
    override val key: K, // The code below is exactly the same for each entry type.
    override val hash: Int,
    override val next: ReferenceEntry<K, V>?
) : ReferenceEntry<K, V> {

    private val _valueReference = atomic<ValueReference<K, V>?>(ValueReference.unset())
    override var valueReference: ValueReference<K, V>? = _valueReference.value
}

private class StrongAccessEntry<K : Any, V : Any>(
    key: K,
    hash: Int,
    next: ReferenceEntry<K, V>?
) :
    StrongEntry<K, V>(key, hash, next) {
    // The code below is exactly the same for each access entry type.

    private val _accessTime = atomic(Long.MAX_VALUE)
    override var accessTime = _accessTime.value

    // Guarded By Segment.this
    override var nextInAccessQueue: ReferenceEntry<K, V> = nullEntry()

    // Guarded By Segment.this
    override var previousInAccessQueue: ReferenceEntry<K, V> = nullEntry()
}

private class StrongWriteEntry<K : Any, V : Any>(
    key: K,
    hash: Int,
    next: ReferenceEntry<K, V>?
) :
    StrongEntry<K, V>(key, hash, next) {
    // The code below is exactly the same for each write entry type.
    private val _writeTime = atomic(Long.MAX_VALUE)
    override var writeTime = _writeTime.value

    // Guarded By Segment.this
    override var nextInWriteQueue: ReferenceEntry<K, V> = nullEntry()

    // Guarded By Segment.this
    override var previousInWriteQueue: ReferenceEntry<K, V> = nullEntry()
}

private class StrongAccessWriteEntry<K : Any, V : Any>(
    key: K,
    hash: Int,
    next: ReferenceEntry<K, V>?
) :
    StrongEntry<K, V>(key, hash, next) {
    // The code below is exactly the same for each access entry type.
    private val _accessTime = atomic(Long.MAX_VALUE)
    override var accessTime: Long = _accessTime.value

    // Guarded By Segment.this
    override var nextInAccessQueue: ReferenceEntry<K, V> = nullEntry()

    // Guarded By Segment.this
    override var previousInAccessQueue: ReferenceEntry<K, V> = nullEntry()

    // The code below is exactly the same for each write entry type.
    private val _writeTime = atomic(Long.MAX_VALUE)
    override var writeTime: Long = _writeTime.value

    // Guarded By Segment.this
    override var nextInWriteQueue: ReferenceEntry<K, V> = nullEntry()

    // Guarded By Segment.this
    override var previousInWriteQueue: ReferenceEntry<K, V> = nullEntry()
}

/**
 * References a strong value.
 */
private open class StrongValueReference<K : Any, V : Any>(private val referent: V) :
    ValueReference<K, V> {
    override fun get(): V = referent
    override val weight: Int = 1
    override val entry: ReferenceEntry<K, V>? = null
    override fun copyFor(value: V?, entry: ReferenceEntry<K, V>?): ValueReference<K, V> = this
    override val isActive: Boolean = true
    override fun notifyNewValue(newValue: V) {}
}

/**
 * References a strong value.
 */
private class WeightedStrongValueReference<K : Any, V : Any>(
    referent: V,
    override val weight: Int
) :
    StrongValueReference<K, V>(referent)